package com.myecl.poker.rules.enums;

import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;

import com.myecl.poker.rules.Card;
import com.myecl.poker.util.Sort;



public enum Hands {
	HIGH_CARD, ONE_PAIR, TWO_PAIR, THREE_OF_A_KIND, STRAIGHT, FLUSH, FULL_HOUSE,
	FOUR_OF_A_KIND, STRAIGHT_FLUSH, ROYAL_FLUSH;
	
	
	
	public Card[] evaluate(List<Card> cards) {
		Card[] result = null;
		switch(this) {
			case HIGH_CARD: result = highCard(cards) ; break;
			case ONE_PAIR: result = onePair(cards) ; break;
			case TWO_PAIR: result = twoPair(cards) ; break;
			case THREE_OF_A_KIND: result = threeOfAKind(cards) ; break;
			case STRAIGHT: result = straight(cards); break;
			case FLUSH: result = flush(cards); break;
			case FULL_HOUSE: result = fullHouse(cards) ; break;
			case FOUR_OF_A_KIND: result = fourOfAKind(cards) ; break;
			case STRAIGHT_FLUSH: result = straightFlush(cards) ; break;
			case ROYAL_FLUSH: result = royalFlush(cards); break;
		}
		return result;
	}
	
	private Card[] highCard(List<Card> cards) {
		return ofAKind(cards, 1);	
	}
	
	private Card[] onePair(List<Card> cards) {
		return ofAKind(cards, 2);	
	}
	
	private Card[] twoPair(List<Card> cards) {
		Card[] result = null;
		if(cards.size() > 3) {
			
			Card[] pair1 = onePair(cards);
			if(pair1 != null) {	
				cards = new LinkedList<Card>(cards);
				cards.removeAll(Arrays.asList(pair1));
				Card[] pair2 = onePair(cards);
		
				if(pair2 != null) {
					result = new Card[4];
					result[0] = pair1[0];
					result[1] = pair1[1];
					result[2] = pair2[0];
					result[3] = pair2[1];
				}
			}
		}
		
		return result;	
	}
	
	private Card[] threeOfAKind(List<Card> cards) {
		return ofAKind(cards, 3);		
	}
	
	private Card[] straight(List<Card> cards) {
		Card[] result = null;
		if(cards.size() > 4) {
			TreeSet<Card> cardsByRank = Sort.byRank(cards);
			LinkedList<Card> straight = new LinkedList<Card>();
			Iterator<Card> iter = cardsByRank.descendingIterator();
			while (iter.hasNext() && straight.size() < 5) {
				Card card = iter.next();
				if (straight.size() == 0) {
					straight.add(card);
					
				} else {
					if (straight.getLast().compareRank(card) == 1) {
						straight.add(card);
					} else if(straight.getLast().compareRank(card) > 1) {
						straight.clear();
						straight.add(card);
					}
				}
			}
			if(straight.size() == 5) {
				result = new Card[5];
				for(int i = 0; i < 5; i++) {
					result[i] = straight.pollFirst();
				}
			}
		}

		return result;
		}
	
	private Card[] flush(List<Card> cards) {

		Card[] result = null;
		if(cards.size() > 4) {
			LinkedList<TreeSet<Card>> cardsBySuit = Sort.bySuit(cards);
			for(TreeSet<Card> t: cardsBySuit) {
				if (t.size() > 4) {
					Iterator<Card> iter = t.descendingIterator();
					if(result == null) {
						result = new Card[5];
						for (int i = 0; i < 5; i++) {
						result[i] = iter.next();
						}
					} else {
						boolean smaller = false;
						for(int i = 0; i < 5 && !smaller; i++) {
							smaller = (result[i].compareRank(iter.next()) < 0);
						}
						if (smaller) {
							for(int i = 0; i < 5; i++) {
								result[i] = t.pollLast();
							}
						}
				}
				}
			}
		}
		return result;
	}

	private Card[] fullHouse(List<Card> cards) {
		Card[] result = null;
		if(cards.size() > 4) {
			Card[] threeOfAKind = threeOfAKind(cards);
			if(threeOfAKind != null) {	
				cards = new LinkedList<Card>(cards);
				cards.removeAll(Arrays.asList(threeOfAKind));
				Card[] pair = onePair(cards);
		
				if(pair != null) {
					result = new Card[5];
					result[0] = threeOfAKind[0];
					result[1] = threeOfAKind[1];
					result[2] = threeOfAKind[2];
					result[3] = pair[0];
					result[4] = pair[1];
				}
			}
		}
		return result;
	}

	private Card[] fourOfAKind(List<Card> cards) {

		return ofAKind(cards, 4);	
	}

	private Card[] straightFlush(List<Card> cards) {
		Card[] result = null;
		LinkedList<TreeSet<Card>> cardsBySuit = Sort.bySuit(cards);
		for(TreeSet<Card> t: cardsBySuit) {
			Card[] straight = straight(new LinkedList<Card>(t));

			if(straight != null && result == null) {
				result = straight;
			} else if (straight != null && result[0].compareTo(straight[0]) < 0 ) {
				result = straight;
			}
		}
		return result;
	}
	
	private Card[] royalFlush(List<Card> cards) {
		Card[] result = null;
		Card[] straightFlush = straightFlush(cards);
		if(straightFlush != null && straightFlush[0].getRank() == Rank.ACE) {
			result = straightFlush;
		}
		return result;
	}
	
	
	
	private Card[] ofAKind(List<Card> cards, int number) {
		Card[] result = null;
		if(cards.size() >= number) {
			LinkedList<TreeSet<Card>> sameRank = Sort.sameRank(cards);
			Iterator<TreeSet<Card>> iter = sameRank.descendingIterator();
			while(iter.hasNext() && result == null) {
				TreeSet<Card> rank = iter.next();
				if(rank.size() >= number) {
					result = new Card[number];
					for(int i = 0; i < number; i++) {
						result[i] = rank.pollLast();
					}
				}
			}
		}
		return result;
	}
}

